module Brewsky
  module BimTools

    # Custom WebDialog wrapper that works around problems with WebDialog#set_html
    # under OSX after Safari 5.0.6 is installed.
    #
    # Example is bare bone without any error checking. Expand as you find fit.
    class WebDialogPatch < UI::WebDialog
    
      # @note Safari 5.0.6 made .set_html unusable under OSX because any links to
      #   resources ( Images, CSS, JS ) on the local computer failed to load.
      #   Previously it would work when you specified file:/// but now it is denied.
      #
      # @param [String] html_string
      # @return [Nil]
      def set_html( html_string )
        # Clean up any old temp file.
        cleanup_temp_file()
        # Finalizer is attached to the webdialog so when it gets garbage collected
        # temp file is erased.
        # 
        # The temp filename needs to be different from the last on in order for the
        # html to be loaded. If the name is the same the content is not refreshed.
        #
        # For both the temp directory and temp file handling with better error
        # handling it'd probably best to port `tmpdir.rb` and `tempfile.rb` from
        # the Standard Ruby Library.
        #
        # http://www.ruby-doc.org/stdlib-1.8.6/
        tempdir = File.expand_path( ENV['TMP'] || ENV['TEMP'] || ENV['TMPDIR'] || "/tmp" )
        unique_seed = "#{self.object_id}#{Time.now.to_i}".hash.abs
        filename = "webdialog_#{unique_seed}.html"
        @tempfile = File.join( tempdir, filename )
        cleanup_proc = self.class.cleanup_temp_file( @tempfile.dup )
        ObjectSpace.define_finalizer( self, cleanup_proc )
        # Write the HTML content out to the temp file.
        File.open( @tempfile, 'w' ) { |file|
          file.write( html_string )
        }
        set_file_original( @tempfile )
        nil
      end
      
      # @tempfile is set to `nil` when using #set_file and #set_url so the temp file
      # will be deleted. Since the #set_html wrapper uses #set_file it must be
      # aliased
      unless private_method_defined?( :set_file_original )
        # Prevent redefining in case of script reloading which cause infinite loop.
        alias :set_file_original :set_file
        private :set_file_original
      end
      # @param [String] filename
      # @return [Nil]
      def set_file( filename )
        cleanup_temp_file()
        set_file_original( filename )
      end
      
      # @param [String] url
      # @return [Nil]
      def set_url( url )
        cleanup_temp_file()
        super
      end
      
      # @return [Nil]
      def cleanup_temp_file
        if @tempfile
          ObjectSpace.undefine_finalizer( @tempfile )
          File.delete( @tempfile ) if File.exist?( @tempfile )
        end
        @tempfile = nil
      end
      private :cleanup_temp_file
      
      # @private
      #
      # @see #set_html
      # @see http://www.mikeperham.com/2010/02/24/the-trouble-with-ruby-finalizers/
      #
      # @param [String] filename
      # @return [Proc]
      def self.cleanup_temp_file( filename )
        proc { File.delete( filename ) if File.exist?( filename ) }
      end
    end # class WebDialogPatch
  end # module BimTools
end # module Brewsky
